package com.socrata.datasync.model; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStreamReader; import java.util.Vector; import javax.swing.table.AbstractTableModel; import au.com.bytecode.opencsv.CSVReader; import com.socrata.datasync.config.controlfile.ControlFile; /** * The CSV Model is used primarily to drive the previews in the * mapping panel, as well as for validation of key failure points * (e.g. date formatting). As discussed in the DatasetModel class, * the actual column names are for display purposes only. The * DatasetModel maintains a separate list of invariant columns that it * manages independent of the values in the CSV. */ public class CSVModel extends AbstractTableModel{ private String[] columnNames; final int rowsToSample = 100; private Vector<Vector<Object>> data = new Vector<>(); public CSVModel(ControlFile file) throws IOException { updateTable(file); } public void updateTable(ControlFile file) throws IOException { data.removeAllElements(); updateColumnNames(file); addSamples(file); } // Return rows added private int addSamples(ControlFile controlFile) throws IOException{ CSVReader reader = getCSVReader(controlFile, controlFile.getFileTypeControl().skip); String [] row = reader.readNext(); int rowsAdded = 0; while (row != null && rowsAdded < rowsToSample){ // The consumers of this class assume a table with an equal number of columns in every row. // If the row is blank, we'll need to get a placeholder with as many columns as the others to allow the // control file editor the ability to load. if (isBlankRow(row)){ insertData(getBlankPlaceholderRow(getColumnCount())); } else { insertData(row); } rowsAdded++; row = reader.readNext(); } return rowsAdded; } private boolean isBlankRow(String[] row){ return row.length == 1 && row[0].isEmpty(); } // This method will create a dummy row with as many columns as exist in the rest of the dataset. // This will allow the control file editor to load, and the customer to skip the first couple of rows // by setting the "skip" option under "advanced options" private String[] getBlankPlaceholderRow(int columns){ String[] placeholder = new String[columns]; return placeholder; } private CSVReader getCSVReader(ControlFile controlFile, int skip) throws IOException{ String path = controlFile.getFileTypeControl().filePath; String encoding = controlFile.getFileTypeControl().encoding; char sep = controlFile.getFileTypeControl().separator.charAt(0); char quote = controlFile.getFileTypeControl().quote.charAt(0); char escape = '\u0000'; InputStreamReader inputReader = new InputStreamReader(new FileInputStream(controlFile.getFileTypeControl().filePath), controlFile.getFileTypeControl().encoding); if (controlFile.getFileTypeControl().escape != null ){ if (controlFile.getFileTypeControl().escape.equals("")) escape = '\u0000'; else escape = controlFile.getFileTypeControl().escape.charAt(0); } CSVReader reader = new CSVReader(inputReader, sep, quote, escape, skip); return reader; } private void updateColumnNames(ControlFile file) throws IOException { boolean hasHeaderRow = file.getFileTypeControl().hasHeaderRow; CSVReader headerReader = getCSVReader(file, 0); String[] row = headerReader.readNext(); if (hasHeaderRow) { columnNames = row; } else{ columnNames = generatePlaceholderNames(row.length); } fireTableStructureChanged(); } private String[] generatePlaceholderNames(int columnCount){ String[] placeholders = new String[columnCount]; for (int i = 0; i < columnCount; i++){ placeholders[i] = ModelUtils.generatePlaceholderName(i); } return placeholders; } @Override public int getColumnCount() { return columnNames.length; } @Override public int getRowCount() { return data.size(); } public int getRowSize(int row){ return data.get(row).size(); } @Override public Object getValueAt(int row, int col) { return data.get(row).get(col); } public String getColumnName(int col){ return columnNames[col]; } public Class getColumnClass(int c){ return getValueAt(0,c).getClass(); } public void setValueAt(Object value, int row, int col){ data.get(row).setElementAt(value, col); fireTableCellUpdated(row,col); } public String getColumnPreview(int columnIndex, int itemsToPreview){ StringBuffer buf = new StringBuffer(); for (int i = 0; i < Math.min(itemsToPreview,getRowCount()); i++){ Object value = "N/A"; try { value = getValueAt(i,columnIndex); } catch (ArrayIndexOutOfBoundsException e){ System.out.println("Row contains different number of columns than expected. Rows with missing data will be displayed as \"N/A\" in the map fields dialog"); } buf.append(value); if (i+1 < Math.min(itemsToPreview,getRowCount())) buf.append(", "); } return buf.toString(); } public boolean isCellEditable(int row, int col){ return false; } private void insertData(Object[] values){ data.add(new Vector<Object>()); for (int i =0; i < values.length; i++){ data.get(data.size() - 1).add(values[i]); } fireTableDataChanged(); } }